﻿using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;

public class ModelTool : Editor
{
    //----------------------------------------------------从配置里读取模型名字，然后将已经准备好的audioClip动态绑定到animatorController上
    static string conName = "Default";
    static int clipsNum = 10;
    //static string[] preStateName = { "attack", "idle", "underattack", "run", "dead", "skill0", "skill1", "skill2", "skill3", "win", "entrance" };
    //static string[] preStateName = { "idle","die" };

    /// <summary>
    /// 转换到自己时存在exit时间的状态名，即只有完成当前状态动作才能执行下一个相同状态，如attack->attack，如果无退出时间，会卡住
    /// 添加新项时，注意要与动画文件（或者动画状态机里的状态）的名字相同
    /// </summary>
    static string[] haveExitStateName = { "idle","attack1","attack","run","stun"};

	//主角模型需要一些特殊处理，如添加刚体等
	static string leadRoleName="m204_bxwz";
    /// <summary>
    /// 配置里面配置动画事件的事件关键字前缀:如攻击事件为atk_xxx;用字典方便检测
    /// </summary>
    static Dictionary<string, string> aniEventKeyWords = new Dictionary<string, string>() { 
        {"idle_","idle"},
        {"attack_","attack"},
        {"die_","die"},
        {"run_","run"},
    };
    //static string[] statename;

    static List<string> modelNames = new List<string>();
    static List<int> actionNums = new List<int>();

    [MenuItem("Tools/AnimationOperation/CreateAnimatorControl（为选中的模型生成控制器）", false, 102)]
    public static void CreateAnimatorControl()
    {
        //解析配置
        //ParseXML();
        //获取里面的动画文件并生成控制器
        Object[] objs=Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
        int length=objs.Length;
        Object obj = null;
        //key:animationClip所在的目录路径 valu:对应的model的name
        Dictionary<string, string> aniPath = new Dictionary<string, string>();
        //获取所有的animation文件夹的路径，用于读取animationClip并在同一目录下生成animatorController
        foreach (Object o in objs) {
            if (o.name.Contains("_")&& o.name.Substring(o.name.LastIndexOf('_')+1) == "animation")
            {
                //Debug.Log(AssetDatabase.GetAssetPath(o));
                //Assets/Resources/Model/boss/m204_bxwz/fbx/animation
                aniPath.Add(AssetDatabase.GetAssetPath(o), o.name.Substring(0,o.name.LastIndexOf('_')));
            }
        }
        //生成动画控制器
        foreach (string path in aniPath.Keys) {//各个animation的文件夹路径
            EditorUtility.DisplayProgressBar("生成动画控制器", path, 1);
            AnimatorController dycon = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(path + "/controller.controller");
            AnimatorStateMachine rooStateMachine = dycon.layers[0].stateMachine;

            //动画剪辑名
            List<string> aniName = new List<string>();
            //动画剪辑文件
            List<AnimationClip> anis = new List<AnimationClip>();
            List<string> statename = new List<string>();
            List<AnimatorState> states = new List<AnimatorState>();
            //任何状态都可切换到的状态，可用于设置默认状态等
            List<int> anystateTrans = new List<int>();
            AnimationClip temp = null;
            //加载动画剪辑
            DirectoryInfo folder = new DirectoryInfo(path);
            foreach (FileInfo ani in folder.GetFiles()) {//animationClip name
                //Debug.Log(ani.FullName);
                //E:\U3dProj\borderExtension\BorderExpansion\borderExpansionProj\Assets\Resources\Model\boss\m204_bxwz\fbx\animation\attack1.anim
                //Debug.Log(ani.Name);
                //attack1.anim
                if (ani.Name.Substring(ani.Name.LastIndexOf('.') + 1) == "anim")
                {
                    aniName.Add(ani.Name.Substring(0, ani.Name.LastIndexOf('.')));
                }
            }

            foreach(string aniname in aniName)//获取已经抽取出来的animationClip
            {
                temp = AssetDatabase.LoadAssetAtPath(string.Format("{0}/{1}.anim", path, aniname), typeof(AnimationClip)) as AnimationClip;
                if (temp != null)
                {
                    anis.Add(temp);
                }
            }
            //绑定配置添加动画事件
            BindAnimationEvent(aniPath[path], anis);

            for (int i = 0; i < anis.Count; i++)
            {
                //Debug.LogError(anis[i]);
                //Debug.LogError(anis[i].name);
                statename.Add(anis[i].name.ToLower());
                string nameLower=anis[i].name.ToLower();
                if (nameLower == "idle" || nameLower == "die" || nameLower == "win")
                {
                    anystateTrans.Add(i);
                }
            }

            AnimatorState tempstate = null;
            //添加状态机状态
            for (int i = 0; i < statename.Count; i++)
            {
                tempstate = rooStateMachine.AddState(statename[i]);
                tempstate.speed = 1f;
                tempstate.motion = anis[i];
                tempstate.iKOnFeet = false;

                states.Add(tempstate);
            }
            if (states.Count <= 0)
            {
                return;
            }
            AnimatorStateTransition trans = null;
            //添加状态转换
            for (int i = 0; i < states.Count; i++)
            {
                //添加变换条件
				dycon.AddParameter(statename[i], AnimatorControllerParameterType.Bool);
                for (int j = 0; j < states.Count; j++)
                {
                    //死亡不能转到其他状态
                    //不同状态间默认没有exit time，这样其他状态才能突然转换到其他状态;
					//相同状态间默认有exittime
                    if (statename[i] != "die") {
                        if ((statename[i] == statename[j]) /*&& CheckHaveExitTime(statename[i])*/)
                        {
                            trans = states[i].AddTransition(states[j], true);
                            if (statename[i] == "run") {//需要快速转换的状态，可以像exitTime一样定义一个数组保存
                                trans.exitTime = 0.02f;
                                trans.duration = 0.02f;
                            }
                        }
                        else
                        {//两两互联，无exitTime
                            trans = states[i].AddTransition(states[j], false);
                            trans.AddCondition(AnimatorConditionMode.If, 0, statename[j]);
                            //无exitTime，且需要立即转换，否则会出现滑步
                            trans.duration = 0f;
                        }
                        //if (statename[i] == "idle" && statename[j] == "idle")
                        //{
                        //    //ilde状态结束默认转到idle,有exit time
                        //    trans = states[i].AddTransition(states[j], true);

                        //}
                        //else if (statename[i] == "run" && statename[j] == "run")
                        //{
                        //    //run状态结束默认转到idle,有exit time
                        //    trans = states[i].AddTransition(states[j], true);
                        //    //转换快点，不然会出现动作一卡一卡的现象
                        //    trans.exitTime = 0.02f;
                        //    trans.duration = 0.02f;
                        //}
                        //else
                        //{//两两互联，无exitTime
                        //    trans = states[i].AddTransition(states[j], false);
                        //    trans.AddCondition(AnimatorConditionMode.If, 0, statename[j]);
                        //}
                    }
                    
                }
            }

            string tempname = "";

            for (int i = 0; i < anystateTrans.Count; i++)
            {
                tempname = statename[anystateTrans[i]];
                if (tempname.ToLower() == "idle")
                {
                    rooStateMachine.defaultState = states[anystateTrans[i]];
                }
                tempstate = states[anystateTrans[i]];
                //dycon.AddParameter(tempname, AnimatorControllerParameterType.Bool);
                if (tempname.ToLower() == "die") {
                    //idle不在anyState里设置转换了，因为die也会转到idle
                    trans = rooStateMachine.AddAnyStateTransition(tempstate);
                }
                trans.AddCondition(AnimatorConditionMode.If, 0, tempname);
            }
            EditorUtility.ClearProgressBar();
            AssetDatabase.Refresh();
        }
    }

    /// <summary>
    /// 判断对应动画状态转到自己时(eg:idle->idle)是否有exitTime
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static bool CheckHaveExitTime(string name) {
        foreach(string str in haveExitStateName)
        {
            if (str == name) {
                return true;
            }
        }
        return false;
    }

    //-----------------------------------------------------自动搜索fbx目录里的所有fbx文件，将绑定的动画文件生成，然后绑定animatorController
    //动画分离出来后，其实对应fbx文件已经可以删除了，新生成的动画已经不再基于fbx了
    //注意事项：每个动作fbx需要以@分割前后缀，而@后面的后缀与其绑定动画名字需要相同 如m2_bear@run 这个fbx的动画名即为run
    [MenuItem("Tools/AnimationOperation/SplitAniFromFbx_Selection（谨慎使用，以免覆盖重要操作）", false, 101)]
    static void CopyAnimationClip()
    {
        string modelName = "";
        string animationClipName = "";
        Object[] objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);//dppassets模式下，目录也会作为一个对象获取
        int length = objs.Length;
        //foreach (Object o in Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets))
        for (int i = 0; i < length; ++i)
        {
            Object o = objs[i];
            if (!(o is GameObject)) continue;
            if (!o.name.Contains("@") || Path.GetExtension(AssetDatabase.GetAssetPath(o.GetInstanceID())).ToLower() != ".fbx")
            {
                //Debug.Log(o.name + Path.GetExtension(AssetDatabase.GetAssetPath(o.GetInstanceID())) + ">>>>>format wrong:don't have @ separator or isn't a fbx file");
                continue;
            }
            //Debug.Log(o.name + Path.GetExtension(AssetDatabase.GetAssetPath(o.GetInstanceID())).ToLower());
            //m204_bxwz@idle.fbx
            GameObject animationFBX = (GameObject)o;
            modelName = o.name.Split('@')[0];
            animationClipName = o.name.Split('@')[1];
            //Debug.Log(AssetDatabase.GetAssetPath(animationFBX));
            //Assets/Resources/Model/boss/m204_bxwz/fbx/m204_bxwz@idle.FBX
            AnimationClip srcClip = AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(animationFBX), typeof(AnimationClip)) as AnimationClip;
            if (srcClip == null) { 
                //EditorUtility.DisplayCancelableProgressBar("异常","抽取animation异常,路径>>>>"+AssetDatabase.GetAssetPath(animationFBX),1);
                Debug.LogError("抽取animation异常,路径>>>>" + AssetDatabase.GetAssetPath(animationFBX));
                continue;
            }
            AnimationClip newClip = new AnimationClip();
            newClip.name = srcClip.name;

            if (!Directory.Exists(AnimationsPath(modelName,animationFBX)))
                Directory.CreateDirectory(AnimationsPath(modelName, animationFBX));

            string animationPath = AnimationsPath(modelName, animationFBX) + newClip.name + ".anim";
            EditorUtility.CopySerialized(srcClip, newClip);
            AssetDatabase.CreateAsset(newClip, animationPath);
            EditorUtility.DisplayProgressBar(string.Format("从fbx抽取动画文件,总共{0}个", length), "抽取" + o.name, length);
            //AnimationClipCurveData[] curveDatas = AnimationUtility.GetAllCurves(srcClip, true);
            //for (int i = 0; i < curveDatas.Length; i++)
            //{
            //    AnimationUtility.SetEditorCurve(newClip, curveDatas[i].path, curveDatas[i].type, curveDatas[i].propertyName, curveDatas[i].curve);
            //}
        }
        EditorUtility.ClearProgressBar();
        AssetDatabase.Refresh();
    }

    public static string AnimationsPath(string modelName,GameObject animationFBX)
    {
        string root = AssetDatabase.GetAssetPath(animationFBX);
        return root.Substring(0, root.LastIndexOf('/') + 1) + modelName+ "_animation/";
    }

    //-----------------合并操作
//	[MenuItem("Tools/AnimationOperation/抽取动画并生成控制器,并绑定到生成的prefab上(一步到位)", false, 100)]
    public static void CreateAniAndCtl()
    {
        CopyAnimationClip();
        CreateAnimatorControl();
		ExportPrefabFromFBX ();
    }

    /// <summary>
    /// 传入模型名及其所有的AnimationClip来绑定动画事件
    /// 还可以自定义动画事件的传入的参数.
    /// 比如act游戏中的多段攻击,这些数据一定是配到表中的,可以把表中的数据导入成Unity的ScriptableObject类型,
    /// 然后打成.asset文件,在把他指定给AnimationEvent的objectReferenceParam参数,
    /// 这样当动画播放攻击帧并检测到攻击事件后直接把本段攻击的配置数据传入游戏运行时,可以减少不少的数据查找操作.
    /// </summary>
    public static void BindAnimationEvent(string modelName,List<AnimationClip> animationClips) {
        EditorUtility.DisplayProgressBar("绑定动画事件", "绑定>>>>" + modelName, 1);
        //key:actionName value:time Point
        Dictionary<string, string> keyFrame;
        ParseXML(out keyFrame,modelName);
        //开始绑定事件（Animation窗口一般animation 帧率为1:00秒30帧)，
        foreach (AnimationClip clip in animationClips) {
            List<AnimationEvent> eventlist = new List<AnimationEvent>();
            foreach (KeyValuePair<string, string> kv in keyFrame)
            {
                //string keyPrefix = kv.Key.Substring(0, kv.Key.IndexOf('_'));
                string[] values= kv.Key.Split('_');
                string keyPrefix = values[0];
                string animationName = string.Format("{0}{1}", keyPrefix, values[1]);

                if (clip.name.Contains(keyPrefix) && clip.name == animationName)
                {
                    AnimationEvent ae = new AnimationEvent();
                    ae.time = float.Parse(kv.Value)/clip.frameRate;
					ae.functionName = string.Format("On{0}", UpperFirstChar(keyPrefix));
                    //目前以animationClip的编号作为参数传入
                    int param = -1;
                    if (!int.TryParse(values[1], out param)) {
                        param = -1;
                    }
                    ae.intParameter = param;
                    eventlist.Add(ae);
                }
            }
            AnimationUtility.SetAnimationEvents(clip, eventlist.ToArray());
        }
    }

	static string UpperFirstChar(string toUpper){
		return toUpper.Substring (0, 1).ToUpper () + toUpper.Substring (1);
	}

    /// <summary>
    /// 动画事件配的是在第几帧添加事件
    /// keyFrame记录了对应的配置名及其value，如attack_1_1:20
    /// </summary>
    /// <param name="keyFrame">attack_1_1,事件所添加到的帧</param>
    /// <param name="modelName">模型名，以fbx里的动画名前缀为准</param>
    public static void ParseXML(out Dictionary<string,string> keyFrame,string modelName)
    {
        keyFrame = new Dictionary<string, string>();
        string loadURL = Application.dataPath + "/Configuration/ModelPara.txt";
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(loadURL);

        XmlNode models = xmlDoc.SelectSingleNode("model");

        foreach (XmlNode model in models)
        {
            XmlElement _model = model as XmlElement;
            if (_model.GetAttribute("name") != null && _model.GetAttribute("name") == modelName) {
                //考虑到同一个动画可能有多个事件，且可能存在多个同类型的动画，如多个攻击动画
                foreach (XmlAttribute xa in _model.Attributes)
                {
                    if (xa.Name.Contains("_") && aniEventKeyWords[xa.Name.Substring(0, xa.Name.IndexOf('_')+1)] != null)
                    {
                        keyFrame.Add(xa.Name, _model.GetAttribute(xa.Name));
                    }
                } 
            }
        }
    }


    [MenuItem("Tools/AnimationOperation/单独将某一个FBX内的所有动画抽取(用于网上找到的不符合现行标准的模型)", false, 103)]
    public static void CreateSingle()
    {
        Object[] objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);//dppassets模式下，目录也会作为一个对象获取
        int length = objs.Length;
        for (int i = 0; i < length; ++i)
        {
            Object o = objs[i];
            GameObject animationFBX = (GameObject)o;
            Object[] clips = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(animationFBX));
            string path = AssetDatabase.GetAssetPath(animationFBX.GetInstanceID()) ;
            path=path.Substring(0, path.LastIndexOf('/') + 1) + "_animation/";
            Debug.Log(path);
            foreach (Object a in clips) {
                AnimationClip ac = a as AnimationClip;
                if (ac != null) {
                    AnimationClip newClip = new AnimationClip();
                    newClip.name = ac.name;
                    if (!Directory.Exists(path))
                        Directory.CreateDirectory(path);

                    string animationPath = path + newClip.name + ".anim";
                    if (ac.GetType() == newClip.GetType())
                    {
                        EditorUtility.CopySerialized(ac, newClip);
                        AssetDatabase.CreateAsset(newClip, animationPath);
                        EditorUtility.DisplayProgressBar(string.Format("从fbx抽取动画文件,总共{0}个", length), "抽取" + o.name, length);
                    }
                    else {
                        Debug.LogError(ac.GetType() + "!=" + newClip.GetType());
                        Debug.LogError(ac.name);
                    }
                    
                }
            }
        }
        EditorUtility.ClearProgressBar();
        AssetDatabase.Refresh();

    }


    /// <summary>
    /// 在生成控制器后，再从fbx抽取出prefab，然后绑定控制器,添加动画事件脚本
    /// </summary>
//	[MenuItem("Tools/Test/单独将某一个FBX内的所有动画抽取(用于网上找到的不符合现行标准的模型)", false, 103)]
	public static void ExportPrefabFromFBX()
    {
		Object[] objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
		int length = objs.Length;
		for (int i = 0; i < length; ++i) {
			Object o = objs [i];
			if (o==null ||!(o is GameObject))
				continue;
			if (!o.name.Contains ("@") || 
				Path.GetExtension (AssetDatabase.GetAssetPath (o.GetInstanceID ())).ToLower () != ".fbx"||
				o.name.Split ('@') [1].ToLower()!="idle"
			) {
//				Debug.LogError ("导出prefab出错"+o.name);
				continue;
			}
			//Debug.Log(o.name + Path.GetExtension(AssetDatabase.GetAssetPath(o.GetInstanceID())).ToLower());
			//m204_bxwz@idle.fbx
			GameObject idleFbx= (GameObject)o;
			string prefabName = o.name.Split ('@') [0].ToLower ();
			string relativePath = AssetDatabase.GetAssetPath (idleFbx);
			relativePath = relativePath.Substring (0, relativePath.LastIndexOf ('/'));
			//获取已经生成的controller
			AnimatorController ac= AssetDatabase.LoadAssetAtPath(relativePath+"/"+prefabName+"_animation/controller.controller",typeof(AnimatorController)) as AnimatorController;
			//为了在fbx目录的上一级创建prefab
			relativePath = relativePath.Substring (0, relativePath.LastIndexOf ('/')+1)+prefabName+".prefab";
//			Debug.Log (relativePath);
			//			Assets/GameResources/Model/role/bear/m2_bear.prefab
			// 将这个模型创建为Prefab
			GameObject prefab = (GameObject)PrefabUtility.CreatePrefab(relativePath, idleFbx);//"相对Assets的路径"
			if(ModelToolWindow.addKeyFrame){
                //自定义的k帧类KeyFrameEvent用法不对，废弃
                //prefab.AddComponent<KeyFrameEvent>();
            }

            if (ModelToolWindow.addRigidbody) {
				Rigidbody rbody = prefab.AddComponent<Rigidbody> ();
				rbody.useGravity = true;
				rbody.isKinematic = false;
				//			rbody.drag = float.PositiveInfinity;
				rbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
				//if (prefab.name != leadRoleName) {
				//	rbody.isKinematic = true;//防止2个刚体一起可以互挤
				//} 
			}

			if (ModelToolWindow.addBoxCollider||ModelToolWindow.addCapsuleCollider) {
				ColliderTool.BindBoxColliderForRole_Obj (prefab, ModelToolWindow.addBoxCollider, ModelToolWindow.addCapsuleCollider);
			}

			Animator animator = prefab.GetComponent<Animator> ();
			if (animator == null) {
				animator = prefab.AddComponent<Animator> ();
			}
			animator.runtimeAnimatorController = ac;
			AssetImporter assetImporter= AssetImporter.GetAtPath (relativePath);
			assetImporter.assetBundleName = "role/" + prefabName;
		}
    }

    
    [MenuItem("Tools/ModelOperation/给选中的模型分配ab名")]
	public static void Run1()
	{
		// Object[] objs = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);
		// foreach (Object o in objs) {
		// 	string path = AssetDatabase.GetAssetPath (o);
		// 	if (path.EndsWith (".prefab")) {
		// 		AssetImporter assetImporter = AssetImporter.GetAtPath (AssetDatabase.GetAssetPath (o));
		// 		assetImporter.assetBundleName = "model/" + o.name;
		// 	}
		// }
	}

	[MenuItem("Tools/ModelOperation/清除选中文件夹中所有动画和控制器文件")]
	public static void Run2()
	{
		 Object[] objs = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);
		 foreach (Object o in objs) {
		 	string path = AssetDatabase.GetAssetPath (o);
			if (path.EndsWith (".anim")||path.EndsWith(".overrideController")||path.EndsWith(".controller")) {
		 		AssetImporter assetImporter = AssetImporter.GetAtPath (AssetDatabase.GetAssetPath (o));
				AssetDatabase.DeleteAsset (path);
		 	}
		 }
	}
}



//-----------old:固定动画名字，读配置生成
//for (int k = 0; k < modelNames.Count; k++)
//{
//    //clipsNum = actionNums[k];
//    conName = modelNames[k];

//    AnimatorController dycon = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(string.Format("Assets/AniController/{0}.controller", conName));
//    AnimatorStateMachine rooStateMachine = dycon.layers[0].stateMachine;

//    List<string> statename = new List<string>();
//    List<AnimatorState> states = new List<AnimatorState>();
//    List<AnimationClip> anis = new List<AnimationClip>();
//    List<int> anystateTrans = new List<int>();
//    AnimationClip temp = null;
//    //加载动画剪辑
//    for (int i = 0; i < preStateName.Length; i++)
//    {
//        temp = AssetDatabase.LoadAssetAtPath(string.Format("Assets/Animation/{0}/{1}.anim", conName, preStateName[i]), typeof(AnimationClip)) as AnimationClip;
//        if (temp != null)
//        {
//            anis.Add(temp);
//        }
//    }

//    for (int i = 0; i < anis.Count; i++)
//    {
//        statename.Add(anis[i].name);
//        if (anis[i].name == "idle" || anis[i].name == "dead" || anis[i].name == "win")
//        {
//            anystateTrans.Add(i);
//        }
//    }

//    AnimatorState tempstate = null;
//    //添加状态机状态
//    for (int i = 0; i < statename.Count; i++)
//    {
//        tempstate = rooStateMachine.AddState(string.Format("{0}", statename[i]));
//        tempstate.speed = 1f;
//        tempstate.motion = anis[i];
//        tempstate.iKOnFeet = false;

//        states.Add(tempstate);
//    }
//    if (states.Count <= 0)
//    {
//        return;
//    }
//    AnimatorStateTransition trans = null;
//    for (int i = 0; i < states.Count; i++)
//    {
//        for (int j = 0; j < states.Count; j++)
//        {
//            dycon.AddParameter(string.Format("{0}", statename[j]), AnimatorControllerParameterType.Trigger);
//            trans = states[i].AddTransition(states[j], false);
//            trans.AddCondition(AnimatorConditionMode.If, 0, string.Format("{0}", statename[j]));
//        }
//    }

//    string tempname = "";

//    for (int i = 0; i < anystateTrans.Count; i++)
//    {
//        tempname = statename[anystateTrans[i]];
//        if (tempname == "idle")
//        {
//            rooStateMachine.defaultState = states[anystateTrans[i]];
//        }
//        tempstate = states[anystateTrans[i]];
//        dycon.AddParameter(string.Format("{0}", tempname), AnimatorControllerParameterType.Bool);
//        trans = rooStateMachine.AddAnyStateTransition(tempstate);
//        trans.AddCondition(AnimatorConditionMode.If, 0, string.Format("{0}", tempname));
//    }


//    states[states.Count - 1].AddExitTransition();
//}
